using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Data.SqlClient;
using System.DirectoryServices;
using System.IO;
using System.ServiceProcess;
using System.Security.Permissions;
using System.Text;

namespace Microsoft.ConnectionEngine.Common.Install
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class Installer : System.Configuration.Install.Installer
    {
        #region Context Parameter constants
        /// <summary>
        /// 
        /// </summary>
        protected const string ROUTING_HOST = "routinghost";
        /// <summary>
        /// 
        /// </summary>
        protected const string CONN_ENG_USER = "connenguser";
        /// <summary>
        /// 
        /// </summary>
        protected const string CONN_ENG_PASS = "connengpass";

        /// <summary>
        /// 
        /// </summary>
        protected const string SPR_HOST = "sprhost";
        /// <summary>
        /// 
        /// </summary>
        protected const string SPR_WS_SITE = "sprwssite";

        /// <summary>
        /// 
        /// </summary>
        protected const string SPR_DB_HOST = "sprdbhost";
        /// <summary>
        /// 
        /// </summary>
        protected const string SPR_DB_NAME = "sprdbname";
        /// <summary>
        /// 
        /// </summary>
        protected const string SPR_DB_USER = "sprdbuser";
        /// <summary>
        /// 
        /// </summary>
        protected const string SPR_DB_PASS = "sprdbpass";

        /// <summary>
        /// 
        /// </summary>
        protected const string DB_ADMIN_USER = "dbadminuser";
        /// <summary>
        /// 
        /// </summary>
        protected const string DB_ADMIN_PASS = "dbadminpass";

        /// <summary>
        /// 
        /// </summary>
        protected const string APP_POOL_NAME = "apppoolname";
        /// <summary>
        /// 
        /// </summary>
        protected const string APP_POOL_USER = "apppooluser";
        /// <summary>
        /// 
        /// </summary>
        protected const string APP_POOL_PASS = "apppoolpass";
        #endregion

        #region Other constants
        /// <summary>
        /// Name of the Default Application Pool in IIS.
        /// </summary>
        public const string DEFAULT_APP_POOL_NAME = "ConnectionEngineAppPool";
        #endregion

        /// <summary>
        /// 
        /// </summary>
        protected virtual void FixParameters()
        {
            string localhostName = this.GetCurrentHostName();

            string value = this.Context.Parameters[ROUTING_HOST];
            if (value != null)
            {
                if ((string.Compare(value, ".") == 0) ||
                    (string.Compare(value, "localhost", true) == 0))
                {
                    this.Context.Parameters[ROUTING_HOST] = localhostName;
                }
            }

            value = this.Context.Parameters[SPR_HOST];
            if (value != null)
            {
                if ((string.Compare(value, ".") == 0) ||
                    (string.Compare(value, "localhost", true) == 0))
                {
                    this.Context.Parameters[SPR_HOST] = localhostName;
                }
            }

            value = this.Context.Parameters[SPR_DB_HOST];
            if (value != null)
            {
                if ((string.Compare(value, ".") == 0) ||
                    (string.Compare(value, "localhost", true) == 0))
                {
                    this.Context.Parameters[SPR_DB_HOST] = localhostName;
                }
            }

            value = this.Context.Parameters[APP_POOL_NAME];
            if (value != null)
            {
                if (value.Length == 0)
                {
                    this.Context.Parameters[APP_POOL_NAME] = DEFAULT_APP_POOL_NAME;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected string InstallFolder
        {
            get
            {
                string assemblyPath = this.Context.Parameters["assemblypath"];

                FileInfo fileInfo = new FileInfo(assemblyPath);
                return fileInfo.DirectoryName;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected string GetCurrentHostName()
        {
            return System.Environment.MachineName;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="parameters"></param>
        protected void SetFileParameters(string path, Dictionary<string, string> parameters)
        {
            string data = ReadDataFromFile(path);

            foreach (KeyValuePair<string, string> parameter in parameters)
            {
                data = data.Replace(parameter.Key, parameter.Value);
            }

            SaveDataToFile(path, data);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        protected string ReadDataFromFile(string path)
        {
            StreamReader reader = new StreamReader(path);

            string data = reader.ReadToEnd();
            reader.Close();

            return data;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="data"></param>
        protected void SaveDataToFile(string path, string data)
        {
            StreamWriter writer = new StreamWriter(path);

            writer.Write(data);
            writer.Close();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webServer"></param>
        /// <returns></returns>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        private bool OSSuppportsApplicationPools(string webServer)
        {
            try
            {
                DirectoryEntry iisApplicationPools = new DirectoryEntry("IIS://" + webServer + "/W3SVC/AppPools");
                
                foreach (DirectoryEntry de in iisApplicationPools.Children)
                {// WorkITem 121 : sajoshi 24/04/2007
                    de.Dispose();
                    break;
                }
            }
            catch (System.Runtime.InteropServices.COMException ex)
            {
                if (ex.ErrorCode == -2147024893)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webServer"></param>
        /// <param name="applicationPoolName"></param>
        /// <returns></returns>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        private DirectoryEntry GetApplicationPool(string webServer, string applicationPoolName)
        {
            if (OSSuppportsApplicationPools(webServer))
            {
                DirectoryEntry iisApplicationPools = new DirectoryEntry("IIS://" + webServer + "/W3SVC/AppPools");

                Dictionary<string, DirectoryEntry> applicationPoolNames = new Dictionary<string, DirectoryEntry>();
                foreach (DirectoryEntry de in iisApplicationPools.Children)
                {
                    applicationPoolNames.Add(de.Name, de);
                }

                if (applicationPoolNames.ContainsKey(applicationPoolName))
                {
                    return applicationPoolNames[applicationPoolName];
                }
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webServer"></param>
        /// <param name="applicationPoolName"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        private void CreateIISApplicationPool(string webServer, string applicationPoolName, string username, string password)
        {
            if (OSSuppportsApplicationPools(webServer))
            {
                DirectoryEntry iisApplicationPools = new DirectoryEntry("IIS://" + webServer + "/W3SVC/AppPools");

                Dictionary<string, DirectoryEntry> applicationPoolNames = new Dictionary<string, DirectoryEntry>();
                foreach (DirectoryEntry de in iisApplicationPools.Children)
                {
                    applicationPoolNames.Add(de.Name, de);
                }

                if (GetApplicationPool(webServer, applicationPoolName) == null)
                {
                    DirectoryEntry newApplicationPool = iisApplicationPools.Children.Add(applicationPoolName, "IIsApplicationPool");
                    newApplicationPool.CommitChanges();

                    if (username.Length > 0)
                    {
                        newApplicationPool.Properties["AppPoolIdentityType"][0] = 3;
                        newApplicationPool.Properties["WAMUserName"][0] = username;
                        newApplicationPool.Properties["WAMUserPass"][0] = password;

                        newApplicationPool.CommitChanges();
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webServer"></param>
        /// <param name="applicationPoolName"></param>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        private void RemoveIISApplicationPool(string webServer, string applicationPoolName)
        {
            if (OSSuppportsApplicationPools(webServer))
            {
                DirectoryEntry iisApplicationPools = new DirectoryEntry("IIS://" + webServer + "/W3SVC/AppPools");

                DirectoryEntry applicationPool = GetApplicationPool(webServer, applicationPoolName);

                if (applicationPool != null)
                {
                    try
                    {
                        iisApplicationPools.Children.Remove(applicationPool);
                    }
                    catch
                    {
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="webServer"></param>
        /// <param name="appName"></param>
        /// <param name="path"></param>
        /// <param name="applicationPoolName"></param>
        /// <param name="appPoolUsername"></param>
        /// <param name="appPoolPassword"></param>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        protected void CreateIISVirtualDir(string webServer, string appName, string path, string applicationPoolName, string appPoolUsername, string appPoolPassword)
        {
            DirectoryEntry iisAdmin = new DirectoryEntry("IIS://" + webServer + "/W3SVC/1/Root");
            string className = iisAdmin.SchemaClassName.ToString();

            if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
            {
                bool canCreateAppPool = OSSuppportsApplicationPools(webServer);
                canCreateAppPool = canCreateAppPool && (applicationPoolName.Length > 0);

                if (canCreateAppPool)
                {
                    CreateIISApplicationPool(webServer, applicationPoolName, appPoolUsername, appPoolPassword);
                }

                RemoveIISVirtualDir(webServer, appName, applicationPoolName);

                DirectoryEntry virtualDirectory = iisAdmin.Children.Add(appName, "IIsWebVirtualDir");

                virtualDirectory.Properties["Path"][0] = path;
                virtualDirectory.Properties["AppFriendlyName"][0] = appName;
                virtualDirectory.Properties["AppRoot"][0] = "/LM/W3SVC/1/Root";
                virtualDirectory.Properties["EnableDirBrowsing"][0] = false;
                virtualDirectory.Properties["AccessRead"][0] = true;
                virtualDirectory.Properties["AccessExecute"][0] = true;
                virtualDirectory.Properties["AccessWrite"][0] = false;
                virtualDirectory.Properties["AccessScript"][0] = true;
                virtualDirectory.Properties["AuthNTLM"][0] = true;
                virtualDirectory.Properties["EnableDefaultDoc"][0] = true;
                virtualDirectory.Properties["DefaultDoc"][0] = "default.htm";
                virtualDirectory.Properties["AspEnableParentPaths"][0] = true;

                if (canCreateAppPool)
                {
                    virtualDirectory.Properties["AppIsolated"][0] = "2";
                }

                virtualDirectory.CommitChanges();

                if ((canCreateAppPool) && (applicationPoolName != null) && (applicationPoolName.Length > 0))
                {
                    object[] createParams = { 0, applicationPoolName, true };

                    virtualDirectory.Invoke("AppCreate3", createParams);
                }
                else
                {
                    virtualDirectory.Invoke("AppCreate", 1);
                }
            }
            else
            {
                throw new Exception("ERROR: Unable to locate the IIS default site or virtual directory node.");
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="webServer"></param>
        /// <param name="appName"></param>
        /// <param name="applicationPoolName"></param>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        protected void RemoveIISVirtualDir(string webServer, string appName, string applicationPoolName)
        {
            DirectoryEntry iisAdmin = new System.DirectoryServices.DirectoryEntry("IIS://" + webServer + "/W3SVC/1/Root");
            string className = iisAdmin.SchemaClassName.ToString();

            if ((className.EndsWith("Server")) || (className.EndsWith("VirtualDir")))
            {
                foreach (DirectoryEntry virtualDirectory in iisAdmin.Children)
                {
                    if (string.Compare(virtualDirectory.Name, appName, true) == 0)
                    {
                        iisAdmin.Invoke("Delete", new string[] { virtualDirectory.SchemaClassName, appName });
                        iisAdmin.CommitChanges();
                        break;
                    }
                }

                bool canRemoveAppPool = OSSuppportsApplicationPools(webServer);
                canRemoveAppPool = canRemoveAppPool && (applicationPoolName.Length > 0);

                if (canRemoveAppPool)
                {
                    RemoveIISApplicationPool(webServer, applicationPoolName);
                }
            }
            else
            {
                throw new Exception("ERROR: Unable to locate the IIS default site or virtual directory node.");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected void ReStartIIS()
        {
            StopIIS();

            StartIIS();
        }

        /// <summary>
        /// 
        /// </summary>
        protected void StopIIS()
        {
            ServiceController iis = new ServiceController("IISADMIN");

            if (null != iis)
            {
                do
                {
                    iis.Refresh();
                }
                while(iis.Status == ServiceControllerStatus.ContinuePending ||
                        iis.Status == ServiceControllerStatus.PausePending ||
                        iis.Status == ServiceControllerStatus.StartPending ||
                        iis.Status == ServiceControllerStatus.StopPending);
                
                if (ServiceControllerStatus.Running == iis.Status ||
                    ServiceControllerStatus.Paused == iis.Status)
                {
                    iis.Stop();
                    iis.WaitForStatus(ServiceControllerStatus.Stopped);
                }

                iis.Close();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected void StartIIS()
        {
            ServiceController iis = new ServiceController("W3SVC");

            if (null != iis)
            {
                do
                {
                    iis.Refresh();
                }
                while (iis.Status == ServiceControllerStatus.ContinuePending ||
                        iis.Status == ServiceControllerStatus.PausePending ||
                        iis.Status == ServiceControllerStatus.StartPending ||
                        iis.Status == ServiceControllerStatus.StopPending);

                if (ServiceControllerStatus.Stopped == iis.Status)
                {
                    iis.Start();
                    iis.WaitForStatus(ServiceControllerStatus.Running);
                }
                else if (ServiceControllerStatus.Paused == iis.Status)
                {
                    iis.Continue();
                    iis.WaitForStatus(ServiceControllerStatus.Running);
                }

                iis.Close();
            }
        }

        /// <summary>
        /// Connects to the Master database and run scripts from there.
        /// If scripts must run from a specific database, adds a USE [databasename] as the inital sql command.
        /// All occurrences of [databasename] will be replaced with the databaseName parameter.
        /// </summary>
        /// <param name="serverName">The database server used to create the connection string.</param>
        /// <param name="databaseName">The database name used to create the connection string.</param>
        /// <param name="username">The username used to create the connection string.</param>
        /// <param name="password">The password used to create the connection string.</param>
        /// <param name="sqlFile">A block of transact sql to be executed.</param>
        /// <returns>Returns a boolean indicating the result of the operation.</returns>
        protected bool ExecuteSQLScript(string serverName, string databaseName, string username, string password, string sqlFile)
        {
            return ExecuteSQLScript(serverName, databaseName, username, password, sqlFile, null);
        }

        /// <summary>
        /// Connects to the Master database and run scripts from there.
        /// If scripts must run from a specific database, adds a USE [databasename] as the inital sql command.
        /// All occurrences of [databasename] will be replaced with the databaseName parameter.
        /// </summary>
        /// <param name="serverName">The database server used to create the connection string.</param>
        /// <param name="databaseName">The database name used to create the connection string.</param>
        /// <param name="username">The username used to create the connection string.</param>
        /// <param name="password">The password used to create the connection string.</param>
        /// <param name="sqlFile">A block of transact sql to be executed.</param>
        /// <param name="parameters">Custom parameters</param>
        /// <returns>Returns a boolean indicating the result of the operation.</returns>
        protected bool ExecuteSQLScript(string serverName, string databaseName, string username, string password, string sqlFile, Dictionary<string, string> parameters)
        {
            Boolean result = false;
            SqlConnection connection = null;

            try
            {
                string sql = this.ReadDataFromFile(sqlFile);
                sql = sql.Replace("[databasename]", databaseName);

                if (parameters != null)
                {
                    foreach (KeyValuePair<string, string> parameter in parameters)
                    {
                        sql = sql.Replace(parameter.Key, parameter.Value);
                    }
                }

                string connectionString = String.Format(@"Data Source={0};Initial Catalog=Master;User Id={1};Password={2};", serverName, username, password);

                connection = new SqlConnection(connectionString);
                SqlCommand cmd = new SqlCommand();
                cmd.CommandType = System.Data.CommandType.Text;
                cmd.Connection = connection;
                connection.Open();

                string[] commands = sql.Split(new string[] { "GO" }, StringSplitOptions.RemoveEmptyEntries);
                for (int x = 0; x < commands.Length; x++)
                {
                    cmd.CommandText = commands[x];
                    cmd.ExecuteNonQuery();
                }

                result = true;
            }
            finally
            {
                if (connection != null)
                {
                    if (connection.State == System.Data.ConnectionState.Open)
                        connection.Close();
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serverName"></param>
        /// <param name="databaseName"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="serviceProviderID"></param>
        /// <returns></returns>
        protected string GetServiceProviderPublicKey(string serverName, string databaseName, string username, string password, int serviceProviderID)
        {
            string result = string.Empty;
            SqlConnection connection = null;

            try
            {
                string connectionString = String.Format(@"Data Source={0};Initial Catalog={1};User Id={2};Password={3};", serverName, databaseName, username, password);

                connection = new SqlConnection(connectionString);
                SqlCommand cmd = new SqlCommand();
                cmd.CommandType = System.Data.CommandType.Text;
                cmd.Connection = connection;
                connection.Open();

                //Work Item ID: 119
                //Modified By: SachinJo
                //using a parameterized Sql query instead of building up the query via string concatenations.
                //cmd.CommandText = "SELECT [publicKey] FROM [ServiceProvider] WHERE [serviceProviderID] = '" + serviceProviderID.ToString() + "'";
                cmd.CommandText = "SELECT [publicKey] FROM [ServiceProvider] WHERE [serviceProviderID] = @serviceProviderID";
                cmd.Parameters.AddWithValue("@serviceProviderID", serviceProviderID);
                
                result = cmd.ExecuteScalar().ToString();
            }
            finally
            {
                if (connection != null)
                {
                    if (connection.State == System.Data.ConnectionState.Open)
                        connection.Close();
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected string GetParamtersForDisplay()
        {
            //Changed by sajoshi 
            //Workitem 117 : String Concat issues has been solved using StringBuilder.
            StringBuilder objBuilder = new StringBuilder();

            foreach (DictionaryEntry parameter in this.Context.Parameters)
            {
                objBuilder.Append("[" + parameter.Key.ToString() + "] = '" + parameter.Value.ToString() + "'");
            }

            return objBuilder.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="savedState"></param>
        /// <returns></returns>
        protected string GetSavedStateForDisplay(IDictionary savedState)
        {
            StringBuilder sb = new StringBuilder();

            foreach (DictionaryEntry item in savedState)
            {
                
                sb.AppendLine("[" + item.Key.ToString() + "] = '" + item.Value.ToString() + "'");
            }

            return sb.ToString();
        }
    }
}
